package com.l2jserver.gameserver.datatables;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.l2jserver.Config;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.util.L2FastMap;

/**
 * This class ...
 * @version $Revision: 1.3.2.2.2.1 $ $Date: 2005/03/27 15:29:18 $
 */
public class CharTable
{
	private static Logger _log = Logger.getLogger(CharTable.class.getName());
	
	private final Map<Integer, String> _chars = new L2FastMap<>();
	private final Map<Integer, Integer> _accessLevels = new L2FastMap<>();
	// by fissban
	// Ampliamos la informacion q podemos obtener por medio del Id de un char.
	private final Map<Integer, Integer> _clan = new L2FastMap<>();
	private final Map<Integer, Integer> _level = new L2FastMap<>();
	private final Map<Integer, Integer> _sex = new L2FastMap<>();
	private final Map<Integer, Integer> _lastAccess = new L2FastMap<>();
	private final Map<Integer, Integer> _pvp = new L2FastMap<>();
	private final Map<Integer, Integer> _pk = new L2FastMap<>();
	private final Map<Integer, Integer> _dead = new L2FastMap<>();
	
	protected CharTable()
	{
		if (Config.CACHE_CHAR_NAMES)
		{
			loadAll();
		}
	}
	
	public final void addName(L2PcInstance player)
	{
		if (player != null)
		{
			addName(player.getObjectId(), player.getName());
			_accessLevels.put(player.getObjectId(), player.getAccessLevel().getLevel());
		}
	}
	
	private final void addName(int objectId, String name)
	{
		if (name != null)
		{
			if (!name.equals(_chars.get(objectId)))
			{
				_chars.put(objectId, name);
			}
		}
	}
	
	public final void removeName(int objId)
	{
		_chars.remove(objId);
		_accessLevels.remove(objId);
		_clan.remove(objId);
		_level.remove(objId);
		_sex.remove(objId);
		_lastAccess.remove(objId);
		_pvp.remove(objId);
		_pk.remove(objId);
		_dead.remove(objId);
	}
	
	/**
	 * Obtenemos el id de un char segun su nombre
	 * @param name
	 * @return
	 */
	public final int getIdByName(String name)
	{
		if ((name == null) || name.isEmpty())
		{
			return -1;
		}
		
		Iterator<Entry<Integer, String>> it = _chars.entrySet().iterator();
		
		Entry<Integer, String> pair;
		while (it.hasNext())
		{
			pair = it.next();
			if (pair.getValue().equalsIgnoreCase(name))
			{
				return pair.getKey();
			}
		}
		
		if (Config.CACHE_CHAR_NAMES)
		{
			return -1;
		}
		
		int id = -1;
		int accessLevel = 0;
		int level = 0;
		int sex = 0;
		int clanId = 0;
		int lastAccess = 0;
		int pvp = 0;
		int pk = 0;
		int dead = 0;
		
		try (Connection con = L2DatabaseFactory.getInstance().getConnection();
			PreparedStatement ps = con.prepareStatement("SELECT charId,accesslevel,level,sex,clanid,lastAccess,pvpkills,pkkills,dead FROM characters WHERE char_name=?"))
		{
			ps.setString(1, name);
			try (ResultSet rs = ps.executeQuery())
			{
				while (rs.next())
				{
					id = rs.getInt(1);
					accessLevel = rs.getInt(2);
					level = rs.getInt(3);
					sex = rs.getInt(4);
					clanId = rs.getInt(5);
					lastAccess = rs.getInt(6);
					pvp = rs.getInt(7);
					pk = rs.getInt(8);
					dead = rs.getInt(9);
				}
			}
		}
		catch (SQLException e)
		{
			_log.log(Level.WARNING, getClass().getSimpleName() + ": Could not check existing char name: " + e.getMessage(), e);
		}
		
		if (id > 0)
		{
			_chars.put(id, name);
			_accessLevels.put(id, accessLevel);
			_level.put(id, level);
			_sex.put(id, sex);
			_clan.put(id, clanId);
			_lastAccess.put(id, lastAccess);
			_pvp.put(id, pvp);
			_pk.put(id, pk);
			_dead.put(id, dead);
			
			return id;
		}
		
		return -1; // not found
	}
	
	/**
	 * Obtenemos el nombre de un char segun su Id
	 * @param id
	 * @return
	 */
	public final String getNameById(int id)
	{
		if (id <= 0)
		{
			return null;
		}
		
		String name = _chars.get(id);
		if (name != null)
		{
			return name;
		}
		
		if (Config.CACHE_CHAR_NAMES)
		{
			return null;
		}
		
		int accessLevel = 0;
		int level = 0;
		int sex = 0;
		int clanId = 0;
		int lastAccess = 0;
		int pvp = 0;
		int pk = 0;
		int dead = 0;
		
		try (Connection con = L2DatabaseFactory.getInstance().getConnection();
			PreparedStatement ps = con.prepareStatement("SELECT char_name,accesslevel,level,sex,clanid,lastAccess,pvpkills,pkkills,dead FROM characters WHERE charId=?"))
		{
			ps.setInt(1, id);
			try (ResultSet rset = ps.executeQuery())
			{
				while (rset.next())
				{
					name = rset.getString(1);
					accessLevel = rset.getInt(2);
					level = rset.getInt(3);
					sex = rset.getInt(4);
					clanId = rset.getInt(5);
					lastAccess = rset.getInt(6);
					pvp = rset.getInt(7);
					pk = rset.getInt(8);
					dead = rset.getInt(9);
				}
			}
		}
		catch (SQLException e)
		{
			_log.log(Level.WARNING, getClass().getSimpleName() + ": Could not check existing char id: " + e.getMessage(), e);
		}
		
		if ((name != null) && !name.isEmpty())
		{
			_chars.put(id, name);
			_accessLevels.put(id, accessLevel);
			_level.put(id, level);
			_sex.put(id, sex);
			_clan.put(id, clanId);
			_lastAccess.put(id, lastAccess);
			_pvp.put(id, pvp);
			_pk.put(id, pk);
			_dead.put(id, dead);
			return name;
		}
		
		return null; // not found
	}
	
	/**
	 * Obtenemos el accesLvl de un char segun su Id
	 * @param objectId
	 * @return
	 */
	public final int getAccessLevelById(int objectId)
	{
		if (getNameById(objectId) != null)
		{
			return _accessLevels.get(objectId);
		}
		
		return 0;
	}
	
	/**
	 * Obtenemos el sex de un char segun su Id
	 * @param objectId
	 * @return (0)Male (1)Famele
	 */
	public final int getCharSexById(int objectId)
	{
		if (getNameById(objectId) != null)
		{
			return _sex.get(objectId);
		}
		
		return 0;
	}
	
	/**
	 * Obtenemos el id de un clan segun el Id del char.
	 * @param objectId
	 * @return
	 */
	public final int getCharClanById(int objectId)
	{
		if (getNameById(objectId) != null)
		{
			return _clan.get(objectId);
		}
		
		return 0;
	}
	
	/**
	 * Obtenemos el Level de un char segun su Id
	 * @param objectId
	 * @return
	 */
	public final int getCharLevelById(int objectId)
	{
		if (getNameById(objectId) != null)
		{
			return _level.get(objectId);
		}
		
		return 0;
	}
	
	/**
	 * Obtenemos cuando se conecto por ultima ves
	 * @param objectId
	 * @return
	 */
	public final int getCharLastAccess(int objectId)
	{
		if (getNameById(objectId) != null)
		{
			return _lastAccess.get(objectId);
		}
		
		return 0;
	}
	
	/**
	 * Obtenemos los pvp de un player
	 * @param objectId
	 * @return
	 */
	public final int getCharPvp(int objectId)
	{
		if (getNameById(objectId) != null)
		{
			return _pvp.get(objectId);
		}
		
		return 0;
	}
	
	/**
	 * Obtenemos los pk de un player
	 * @param objectId
	 * @return
	 */
	public final int getCharPk(int objectId)
	{
		if (getNameById(objectId) != null)
		{
			return _pk.get(objectId);
		}
		
		return 0;
	}
	
	/**
	 * Obtenemos las muertes en pvp de un player
	 * @param objectId
	 * @return
	 */
	public final int getCharDead(int objectId)
	{
		if (getNameById(objectId) != null)
		{
			return _dead.get(objectId);
		}
		
		return 0;
	}
	
	public synchronized boolean doesCharNameExist(String name)
	{
		boolean result = true;
		try (Connection con = L2DatabaseFactory.getInstance().getConnection();
			PreparedStatement ps = con.prepareStatement("SELECT account_name FROM characters WHERE char_name=?"))
		{
			ps.setString(1, name);
			try (ResultSet rs = ps.executeQuery())
			{
				result = rs.next();
			}
		}
		catch (SQLException e)
		{
			_log.log(Level.WARNING, getClass().getSimpleName() + ": Could not check existing charname: " + e.getMessage(), e);
		}
		return result;
	}
	
	public int accountCharNumber(String account)
	{
		int number = 0;
		try (Connection con = L2DatabaseFactory.getInstance().getConnection();
			PreparedStatement ps = con.prepareStatement("SELECT COUNT(char_name) FROM characters WHERE account_name=?"))
		{
			ps.setString(1, account);
			try (ResultSet rset = ps.executeQuery())
			{
				while (rset.next())
				{
					number = rset.getInt(1);
				}
			}
		}
		catch (SQLException e)
		{
			_log.log(Level.WARNING, getClass().getSimpleName() + ": Could not check existing char number: " + e.getMessage(), e);
		}
		return number;
	}
	
	private void loadAll()
	{
		String name;
		int id = -1;
		int accessLevel = 0;
		int level = 0;
		int sex = 0;
		int clanId = 0;
		int lastAccess = 0;
		int pvp = 0;
		int pk = 0;
		int dead = 0;
		
		try (Connection con = L2DatabaseFactory.getInstance().getConnection();
			Statement s = con.createStatement();
			ResultSet rs = s.executeQuery("SELECT charId,char_name,accesslevel,level,sex,clanid,lastAccess,pvpkills,pkkills,dead FROM characters"))
		{
			while (rs.next())
			{
				id = rs.getInt(1);
				name = rs.getString(2);
				accessLevel = rs.getInt(3);
				level = rs.getInt(4);
				sex = rs.getInt(5);
				clanId = rs.getInt(6);
				
				_chars.put(id, name);
				_accessLevels.put(id, accessLevel);
				_level.put(id, level);
				_sex.put(id, sex);
				_clan.put(id, clanId);
				_lastAccess.put(id, lastAccess);
				_pvp.put(id, pvp);
				_pk.put(id, pk);
				_dead.put(id, dead);
				
			}
		}
		catch (SQLException e)
		{
			_log.log(Level.WARNING, getClass().getSimpleName() + ": Could not load char name: " + e.getMessage(), e);
		}
		_log.info(getClass().getSimpleName() + ": Loaded " + _chars.size() + " char names.");
	}
	
	public static CharTable getInstance()
	{
		return SingletonHolder._instance;
	}
	
	private static class SingletonHolder
	{
		protected static final CharTable _instance = new CharTable();
	}
}
